package Parser;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Map.Entry;

import Database.Article;
import Database.Author;
import Database.Book;
import Database.Paper;

public class BibtexFile
{
	static private final int BUFFER_LENGTH = 1024;
	static private final String ABBREV_NAME = "string";
	private BufferedInputStream inputStream;
	private HashMap<String, HashMap<String, String>> typesValuesMap;

	public BibtexFile(String fileName)
	{
		try
		{
			FileInputStream fs;
			fs = new FileInputStream(fileName);
			this.inputStream = new BufferedInputStream(fs);
		} catch (FileNotFoundException e)
		{
			System.err.println("File " + fileName + " not found");
			this.inputStream = null;
		}

		typesValuesMap = new HashMap<String, HashMap<String, String>>();
	}
	
	public BibtexFile(File file)
	{
		try
		{
			FileInputStream fs;
			fs = new FileInputStream(file);
			this.inputStream = new BufferedInputStream(fs);
		} catch (FileNotFoundException e)
		{
			System.err.println("File " + file.getName() + " not found");
			this.inputStream = null;
		}

		typesValuesMap = new HashMap<String, HashMap<String, String>>();
	}

	public void close()
	{
		if (this.inputStream == null)
		{
			System.err.println("The file is not opened");
			return;
		}

		try
		{
			inputStream.close();
		} catch (IOException e)
		{
			System.err.println("Error closing file");
		}
	}

	private enum Level
	{
		TYPE, KEY, VALUE, INSIDE_COMMAS, INSIDE_BRACES
	};

	public void parse()
	{
		if (this.inputStream == null)
		{
			System.err.println("The file is not opened");
			return;
		}

		byte buffer[] = new byte[BUFFER_LENGTH];
		int count;

		Level level = Level.TYPE;
		String key = "";
		String value = "";
		String otherKey = "";
		String type = "";
		HashMap<String, String> currentMap = null;

		try
		{
			while ((count = inputStream.read(buffer)) != -1)
			{
				for (int i = 0; i < count; ++i)
				{
					char c = (char) buffer[i];
					if (c == '@')
					{
						level = Level.TYPE;
						continue;
					}

					if (level == Level.TYPE)
					{
						if (!Character.isWhitespace(c))
						{
							if (c == '{')
							{
								level = Level.KEY;
								type = new String(value);
								currentMap = new HashMap<String, String>();
								if (type.toLowerCase().equals(ABBREV_NAME))
								{
									typesValuesMap.put(type.toLowerCase(),
											currentMap);
									type = "";
								}
								value = "";
							} else if (c != '}')
								value += c;
						}
						continue;
					}

					if (level == Level.KEY)
					{
						if (c == '}')
						{
							if (!key.equals(""))
							{
								typesValuesMap.put(key.toLowerCase(),
										currentMap);
								currentMap.put("type", type.toLowerCase());
								type = "";
								key = "";
							}
							level = Level.TYPE;
							continue;
						}

						if (c == ',')
						{
							typesValuesMap.put(key.toLowerCase(),
								currentMap);
							currentMap.put("type", type.toLowerCase());
							type = "";
							key = "";
							continue;
						}

						if (c == '=')
						{
							level = Level.VALUE;
							continue;
						}

						if (!Character.isWhitespace(c))
						{
							key += c;
							continue;
						}
					}

					if (level == Level.VALUE)
					{
						if (c == '}')
						{
							if (!otherKey.equals(""))
							{
								HashMap<String, String> stringMap = typesValuesMap
										.get(ABBREV_NAME);
								value += stringMap.get(otherKey.toLowerCase());
								otherKey = "";
							}
							currentMap.put(key.toLowerCase(), value);
							key = "";
							value = "";
							level = Level.TYPE;
							continue;
						}

						if (c == ',')
						{
							if (!otherKey.equals(""))
							{
								HashMap<String, String> stringMap = typesValuesMap
										.get(ABBREV_NAME);
								value += stringMap.get(otherKey.toLowerCase());
								otherKey = "";
							}
							currentMap.put(key.toLowerCase(), value);
							key = "";
							value = "";
							level = Level.KEY;
							continue;
						}

						if (c == '{')
						{
							level = Level.INSIDE_BRACES;
							continue;
						}

						if (c == '\"')
						{
							level = Level.INSIDE_COMMAS;
							continue;
						}

						if (c == '#')
						{
							if (!otherKey.equals(""))
							{
								HashMap<String, String> stringMap = typesValuesMap
										.get(ABBREV_NAME);
								value += stringMap.get(otherKey.toLowerCase());
								otherKey = "";
							}
							continue;
						}

						if (!Character.isWhitespace(c))
						{
							otherKey += c;
							continue;
						}
					}

					if (level == Level.INSIDE_COMMAS)
					{
						if (c == '\"')
						{
							level = Level.VALUE;
							continue;
						}

						value += c;
					}

					if (level == Level.INSIDE_BRACES)
					{
						if (c == '}')
						{
							level = Level.VALUE;
							continue;
						}

						value += c;
					}
				}
			}
		} catch (IOException e)
		{
			System.err.println("Error while reading from file");
		}

		printMap();
	}
	
	ArrayList<Paper> getPaperList()
	{
		ArrayList<Paper> list = new ArrayList<Paper>();
		
		if (typesValuesMap == null)
			return list;
		
		
		java.util.Iterator<Entry<String, HashMap<String, String>>> it = typesValuesMap
		.entrySet().iterator();

		while (it.hasNext())
		{
			Map.Entry pairs = it.next();
			
			System.out.println("@ " + pairs.getKey());
			if (pairs.getKey().equals(ABBREV_NAME))
				continue;
			
			HashMap<String, String> currentMap = (HashMap<String, String>) pairs
					.getValue();

			java.util.Iterator<Entry<String, String>> i = currentMap.entrySet()
					.iterator();
			
			boolean isBook = false;
			String title = "";
			String journal = "";
			String publisher = "";
			String author = "";

			while (i.hasNext())
			{
				Map.Entry key = i.next();

				if (key.getKey().equals("type"))
					isBook = key.getValue().equals("book");
				
				if (key.getKey().equals("title"))
					title = (String) key.getValue();
				
				if (key.getKey().equals("journal"))
					journal = (String) key.getValue();
				
				if (key.getKey().equals("publisher"))
					publisher = (String) key.getValue();
								
				if (key.getKey().equals("author"))
					author = (String) key.getValue();
				
				System.out.println("    " + key.getKey() + " = "
						+ key.getValue());
			}

			Paper paper;
			
			if (isBook)
				paper = new Book(title, publisher);
			else
				paper = new Article(title, journal);
			
			StringTokenizer st = new StringTokenizer(author, ";,");
			while (st.hasMoreTokens())
			{
				String token = new String(st.nextToken());
				while (Character.isWhitespace(token.charAt(0)))
					token = token.substring(1, token.length());
				while (Character.isWhitespace(token.charAt(token.length() - 1)))
					token = token.substring(0, token.length());
				Author authorObject = new Author(token, "");
				paper.addAuthor(authorObject);
			}
			
			list.add(paper);
		}
		
		return list;
	}

	void printMap()
	{
		java.util.Iterator<Entry<String, HashMap<String, String>>> it = typesValuesMap
				.entrySet().iterator();

		while (it.hasNext())
		{
			Map.Entry pairs = it.next();

			System.out.println("@ " + pairs.getKey());
			HashMap<String, String> currentMap = (HashMap<String, String>) pairs
					.getValue();

			java.util.Iterator<Entry<String, String>> i = currentMap.entrySet()
					.iterator();

			while (i.hasNext())
			{
				Map.Entry key = i.next();

				System.out.println("    " + key.getKey() + " = "
						+ key.getValue());
			}
		}
	}
}